// Copyright 2007 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lichen.hibernate;

import java.io.IOException;

import lichen.LichenConstants;
import lichen.internal.AnnotationUtil;
import lichen.internal.hibernate.EntityServiceImpl;
import lichen.internal.hibernate.HibernateSessionFactoryImpl;
import lichen.internal.hibernate.SessionManagedImpl;

import org.apache.tapestry.internal.InternalConstants;
import org.apache.tapestry.ioc.Configuration;
import org.apache.tapestry.ioc.IOCConstants;
import org.apache.tapestry.ioc.OrderedConfiguration;
import org.apache.tapestry.ioc.ServiceBinder;
import org.apache.tapestry.ioc.annotations.Inject;
import org.apache.tapestry.ioc.annotations.InjectService;
import org.apache.tapestry.ioc.annotations.Match;
import org.apache.tapestry.ioc.annotations.Scope;
import org.apache.tapestry.ioc.annotations.Symbol;
import org.apache.tapestry.ioc.services.PropertyShadowBuilder;
import org.apache.tapestry.ioc.services.ThreadCleanupHub;
import org.apache.tapestry.services.AliasContribution;
import org.apache.tapestry.services.Request;
import org.apache.tapestry.services.RequestFilter;
import org.apache.tapestry.services.RequestGlobals;
import org.apache.tapestry.services.RequestHandler;
import org.apache.tapestry.services.Response;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;

/**
 * 对hibernate的集成模块.
 * @author jcai
 * @version $Revision: 173 $
 * @since 0.0.3
 */
public class HibernateModule {
	/**
	 * bind service.
	 * @param binder service binder
	 * @since 0.0.3
	 */
    public static void bind(ServiceBinder binder)
    {
        binder.bind(HibernateSessionFactory.class, HibernateSessionFactoryImpl.class);
        binder.bind(EntityService.class, EntityServiceImpl.class);
    }
    /**
     * 构造线程级的受管理的session对象.
     * @param SessionFactory session factory.
     * @param threadCleanupHub thread clean hub.
     * @return 受管理的session对象.
     * @since 0.0.3
     */
	@Scope(IOCConstants.PERTHREAD_SCOPE)
	public static SessionManaged build(@InjectService("SessionFactory")SessionFactory SessionFactory,
            ThreadCleanupHub threadCleanupHub)
    {
		SessionManaged service = new SessionManagedImpl(SessionFactory);
        threadCleanupHub.addThreadCleanupListener(service);

        return service;
    }
	
	/**
	 * 构造hibernate的session对象.
	 * @param sessionManager session manager.
	 * @param propertyShadowBuilder builder
	 * @return Hibernate Session 对象.
	 * @since 0.0.3
	 * @see Session
	 */
    public static Session buildSession(SessionManaged sessionManager,
            PropertyShadowBuilder propertyShadowBuilder)
    {
        return propertyShadowBuilder.build(sessionManager, "session", Session.class);
    }
    /**
     * Contributes the package "&lt;root&gt;.entities" to the configuration, so that it will be
     * scanned for annotated entity classes.
     */
    public static void contributeHibernateSessionFactory(Configuration<String> configuration,
    @Inject
    @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
    String appRootPackage)
    {
        configuration.add(appRootPackage + ".entities");
    }
    
    /**
     * build session factory from {@link HibernateSessionFactory} 对象
     * @param factory hibernate session factory.
     * @param propertyShadowBuilder property builder
     * @return session factory
     * @since 0.0.3
     * @see SessionFactory
     */
    public static SessionFactory build(HibernateSessionFactory factory,
            PropertyShadowBuilder propertyShadowBuilder)
    {
        return propertyShadowBuilder.build(factory, "sessionFactory", SessionFactory.class);
    }
    
    /**
     * 构造全局可以使用的hibernate template 对象.
     * @param sessionFactory session factory
     * @return hibernate template object.
     * @since 0.0.3
     */
    public static HibernateTemplate buildHibernateTemplate(
    		@InjectService("SessionFactory")SessionFactory sessionFactory){
    	return new HibernateTemplate(sessionFactory);
    }
    /**
     * 构造全局使用的transaction manager 对象.
     * @param sessionFactory hibernate session factory.
     * @return PlatformTransaction manager.
     * @since 0.0.3
     * @see PlatformTransactionManager
     * @see HibernateTransactionManager
     */
    public static PlatformTransactionManager buildPlatformTransactionManager(
    		@InjectService("SessionFactory") SessionFactory sessionFactory){
    	PlatformTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
    	return transactionManager;
    }
    /**
     * Contributes the {@link #build(HibernateSessionFactory, PropertyShadowBuilder)}
     * service.
     */
    public static void contributeAlias(Configuration<AliasContribution> configuration,
    @InjectService("SessionFactory")
    SessionFactory sessionFactory,
    @InjectService("PlatformTransactionManager")
    PlatformTransactionManager transactionManager,
    @InjectService("Session") Session session
    )
    {
        configuration.add(AliasContribution.create(SessionFactory.class, sessionFactory));
        configuration.add(AliasContribution.create(PlatformTransactionManager.class, transactionManager));
        configuration.add(AliasContribution.create(Session.class, session));
    }
    /**
     * 对含有 {@link Transactional}注释的service类，采用AOP的策略,自动加上事务处理代码.
     * @since 0.0.3
     * @see Transactional
     * @see AnnotationTransactionAttributeSource
     * @see TransactionProxyFactoryBean
     */
    @Match("*")
    public static <T> T decorateTransactionService(Class<T> serviceInterface, T delegate, 
      String serviceId,@InjectService("PlatformTransactionManager") PlatformTransactionManager transactionManager)
    {	
    	//只处理类中加入了Transactional注释的类.
    	if(AnnotationUtil.isAnnotationPresent(delegate.getClass(),Transactional.class)){
    		TransactionProxyFactoryBean factoryBean=new TransactionProxyFactoryBean();
	    	factoryBean.setTarget(delegate);
	    	factoryBean.setTransactionManager(transactionManager);
			TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
			factoryBean.setTransactionAttributeSource(transactionAttributeSource );
			factoryBean.afterPropertiesSet();
			return serviceInterface.cast(factoryBean.getObject());
    	}
        return delegate;
    } 
	/**
	 * 表单输入也采用UTF-8信息
	 * 
	 * @param configuration  配置.
	 * @param encodingFilter 编码的过滤器.
	 */
	public void contributeRequestHandler(
			OrderedConfiguration<RequestFilter> configuration,
			@InjectService("OpenSessionInThreadFilter")
			RequestFilter encodingFilter) {
		configuration.add("OpenSessionInThreadFilter", encodingFilter);
	}

	/**
	 * 创建一个编码的过滤器
	 * 
	 * @param requestGlobals
	 *            全局请求的Request.
	 * @return 请求过滤器.
	 */
	public RequestFilter buildOpenSessionInThreadFilter(
			final RequestGlobals requestGlobals,
			final SessionManaged sessionManaged, @Inject
			@Symbol(LichenConstants.DEFAULT_ENCODING)
			final String encoding) {

		return new RequestFilter() {
			public boolean service(Request request, Response response,
					RequestHandler handler) throws IOException {
				// 触发线程级的service的load,保证整个请求为一个session.
				sessionManaged.getSession();
				requestGlobals.getHTTPServletRequest().setCharacterEncoding(
						encoding);
				return handler.service(request, response);
			}
		};
	}
}
